//este objeto se encarga de la presentacion de los objetos,
//asi como tambien la generacion y actualizacion de los elementos del DOM

//para la generacion de los objetos 3D, requiere un 'builder' de mallas.
//se podra personalizar la generaci�n de objetos, cambiando builder 
//(por ej builder que genera figuras opacas vs un builder que genera figuras transparentes)
builderSceneObjects = function(meshBuilder, materialBuilder, sceneWrapper){
	
	var _meshBuilder = meshBuilder;
	var _materialBuilder = materialBuilder;
	var _sceneWrapper = sceneWrapper;
	//TODO: esta funcion moverla a alguna UI q mantenga el estado GLOBAL de la aplicacion
	//cuando termino de generar la representacion visual de los spaces (que se incorporaron recientemente)
//	this.cargaSpacesIniciada = function(){
//		$("#progresoCarga").css('display','inline');
//	};
//	this.cargaSpacesFinalizada = function(){
//		$("#progresoCarga").css('display','none');
//	};
	this.generateInterFloorPortal = function(objeto, spaceType){
		
		
		var params = {};
		
		if (objeto.clase == "inter_floor"){
			if (objeto.contourEsLinea()){
				//console.log('es lineaaaaaaaaaaaaa-> ',objeto)
				params.puntos = objeto.contourScaled;
			}else{
				if (objeto.dangling){
					params.puntos = objeto.contourScaled;
				}else{
					params.puntos = objeto.contour;
				}
				
			}
		}else{
			params.puntos = objeto.contour;
		}
		
		var nivel = (spaceType == "source")? objeto.sourceSpace.piso.level : objeto.targetSpace.piso.level; 
		
		if (objeto.dangling){
			params.material = _materialBuilder.getMaterial("portal_externo_dangling", nivel);
		}else{
			params.material = _materialBuilder.getMaterial("portal_externo", nivel);
		}
		
		params.bevelEnabled = true;
		
		if (DEBUG_SHAPES){
			console.log('-------> ENTRO entrando a triangulate', objeto, params.puntos);
			triangulateShape( params.puntos, [])
			console.log('#######> SALGO DE  triangulate');
		}
		var mesh = _meshBuilder.getShapeMesh(params); //TODO mesh builder
		mesh.position.y = objeto.sourceSpace.height;//TODO
		
		return mesh;
	}
	
	
	
	
	
	this.generateLevelRepresentation = function(level){
		var nivel = new THREE.Object3D();
//		var nivel = new THREE.Object3D();
		nivel.TFCpisos = {};
		nivel.TFCid = level.indice;
		nivel.TFCwalls = null;
		
		var unMergedMeshes = [];
		var unMergedWalls = [];
		
		var pisos = level.pisos;
		var obj = null;
		
		//genero la data de cada piso del nivel
		for (var i = 0, l = pisos.length; i < l ; i++){
			obj = generateFloorRepresentation(pisos[i]);
			unMergedMeshes = unMergedMeshes.concat(obj.unMergedMeshes);//spaces y portales
			unMergedWalls = unMergedWalls.concat(obj.unMergedWalls);//paredes
			
			nivel.TFCpisos[obj.data.TFCfloorName] = obj.data;
			
			for (var j = 0, l2 = obj.data.TFCsprites.length; j < l2; j++){
				nivel.add(obj.data.TFCsprites[j]);
			}
		}
		
		
		
		
		//MERGEO spaces y portales
		var mergedGeo = new THREE.Geometry(); // aca guardo la geometria mergeada

		for (var i = 0, l = unMergedMeshes.length; i < l; i++){		
			merge(mergedGeo, unMergedMeshes[i]); //esta funcion esta definida en "GeometryMerger" (es una modificacion del la implementacion de THREE)
		};
		
		var mergedMesh = _meshBuilder.getFaceMaterialMesh(mergedGeo);
		
		nivel.add(mergedMesh);
		mergedMesh = null;
		
		
		//MERGEO PAREDES
		var mergedGeo = new THREE.Geometry(); // aca guardo la geometria mergeada

		for (var i = 0, l = unMergedWalls.length; i < l; i++){		
			merge(mergedGeo, unMergedWalls[i]); //esta funcion esta definida en "GeometryMerger" (es una modificacion del la implementacion de THREE)
		};
		
		mergedMesh = _meshBuilder.getFaceMaterialMesh(mergedGeo);
		nivel.TFCwalls = mergedMesh;
	//	mergedMesh.position.y = 200;
		mergedMesh.doubleSided = true;
		mergedMesh = null;
		
		
		
		if (SOMBRA){
			mergedMesh.castShadow = true;
			mergedMesh.receiveShadow = true;
		}
//		objeto.material.transparent = true;
//		objeto.material.overdraw = true;
	
		
		//++++++++++++++ API NIVEL ++++++++++++++
		nivel.hideSprites = function(){
			//console.log('nivel.hideSprites',this);
			for (var idPiso in this.TFCpisos){
				var piso = this.TFCpisos[idPiso];
				
				for (var j = 0, l2 = piso.TFCsprites.length; j < l2; j++){
					piso.TFCsprites[j].visible = false;
				}
			}
		}
		
		nivel.showSprites = function(){
			//console.log('nivel.showSprites',this);
			for (var idPiso in this.TFCpisos){
				var piso = this.TFCpisos[idPiso];
				
				for (var j = 0, l2 = piso.TFCsprites.length; j < l2; j++){
					piso.TFCsprites[j].visible = true;
				}
			}
		}
		
		nivel.showWalls = function(){
			if (nivel.TFCwalls) UTILS.setOpacity(nivel.TFCwalls, 1);
		};
		
		nivel.hideWalls = function(){
			if (nivel.TFCwalls) UTILS.setOpacity(nivel.TFCwalls, 0);
		};
		//++++++++++++++ fin API NIVEL ++++++++++++++
		
		if (!_sceneWrapper.STATE.SHOW_WALLS){nivel.hideWalls();} //TODO TODO TODO TODO TODO TODO TODO  (descomentar esta linea)
		
		return nivel;
	};
	
	
	
	
	
	
	//dada la informacion de un piso, se crean el piso (objeto 3D), spaces, portales,
	//textos3D, landmarks; y se provee a este objeto de punteros para poder acceder
	//a estos objetos de manera mas sencilla.
	function generateFloorRepresentation(floor){
		
		var unMergedMeshes = [];
		var sprites = []; //ale
		var spritesBySpace = []; //jeib
		//var stairs = [];
		//var stairsByID = {}; //redundante, por ahora lo dejo
		var landmarks = [];
		var unMergedWalls = [];
		
		//+++ SPACES +++
		var obj = generateSpaces(floor.spaces, floor.portals);
		unMergedMeshes = obj.unMergedMeshes;
		
		sprites = obj.sprites; //ale
		spritesBySpace = obj.spritesBySpace;//jesus
		unMergedWalls = unMergedWalls.concat(obj.paredes);
		
		
		//+++ PORTALS +++
		obj = generatePortals(floor.portals);
		//console.log("en generateFloorRepresentation --> obj.unMergedMeshes", obj.unMergedMeshes)
		unMergedMeshes = unMergedMeshes.concat(obj.unMergedMeshes);
		unMergedMeshes = unMergedMeshes.concat(obj.stairs);//mergear escaleras???
		unMergedWalls = unMergedWalls.concat(obj.unMergedWalls);
		
		//+++ data de cada floor +++
		var data = {};
		data.TFCsprites = sprites;
		data.TFCspritesBySpace = spritesBySpace;
		data.TFCfloorName = floor.name;
		
		
		return {unMergedMeshes: unMergedMeshes, unMergedWalls: unMergedWalls, data: data};
	};
	
	
	function generateLandmarks(landmarks){
		
		if (!landmarks || !landmarks.length) return [];
		
		console.log("LANDKMARKS --> falta arreglar");
		return [];
		//dibujamos el lamdmark y lo a�adimos como objeto 'hijo' del piso al cual corresponde
		this.object3D = null;
		
		//tiene que ser asincronica porque tiene que cargar la imagen
		_meshBuilder.getLandmarkMesh({landmark: this, parentObject :  piso.objeto3D, nivel: piso.level});
		
		this.setObjeto3D= function(obj){
			this.object3D = obj;										
					
			//agrego el landmark a la coleccion de landmarks por tipo (de toda la escena)
			if (!_landmarks[this.type]){
				_landmarks[this.type] = [];
			}
			_landmarks[this.type].push(obj);	
		};
	};
	
	function generateSpaces(spaces){
		
		var unMergedSpaceMeshes = []; //mallas de los spaces que vamos a mergear para mejorar eficiencia
		var unMergedWalls = [];
		var sprites = [];
		var spritesBySpace = {};
		
		var obj3D = null;
		var space = null;
		for (var spaceName in spaces){
			
			space = spaces[spaceName];
			var nivel = space.piso.level;
			
			//+++ SHAPE PRINCIPAL +++
			var params = {};
			params.puntos = space.contour.points; 
			params.bevelEnabled = true;
			params.material =  _materialBuilder.getMaterial(space.type, nivel);
			params.extrudeValue = (space.type == "tr")? _sceneWrapper.CONSTANTS.EXTRUDE_CORRIDOR : _sceneWrapper.CONSTANTS.EXTRUDE_OTHERS;
			obj3D = _meshBuilder.getShapeMesh(params);
			
			unMergedSpaceMeshes.push(obj3D);
			
			
			//+++ SPRITES +++
			if(space.name != ""){
				var centroid = space.contour.centroid;
				var altoSprite = _sceneWrapper.CONSTANTS.SPRITE_HEIGHT  || 0;
				var pos = {x: centroid.x, y:centroid.y + altoSprite , z: centroid.z};
				if(space.type !="tr"){pos.y+= _sceneWrapper.CONSTANTS.EXTRUDE_OTHERS;}
				
				params = {};
				params.posicion = pos; 
				params.texto = space.name;
				params.initialScale = 0.75;
				var obj = _meshBuilder.getCenteredSprite(params);
				
				spritesBySpace[spaceName] = obj;
				sprites.push(obj); 
			}
			
			//+++ PAREDES Y TECHO +++
			//space.id == "18" && space.piso.id == "CA_PB"
			if (_sceneWrapper.CONSTANTS.PAREDES){
				
				var altura = 0;
				if (space.height < space.piso.referenceNode.height){
					altura = space.piso.referenceNode.height - space.height;
				}else{
//					altura = space.piso.referenceNode.height + 50  - space.height;
					altura = (nivel+1)*100 - space.height;
				} 
				if (altura <= 0)	console.log('altura', altura, 'data',space)
				
				//+++ PAREDES +++
				for (var i = 0, l = space.paredes.length; i < l; i++){
					
					var params = {};
					params.puntos = space.paredes[i];
					
					//si le pongo el mismo material que el piso, cuando quiero hacer invisibles las paredes, se hace invisible tambien el piso
					params.material = _materialBuilder.getMaterial("pared" /*space.type*/, nivel);
					params.altura = altura; //30
					var mesh = _meshBuilder.getWallMesh(params);
					
					unMergedWalls.push(mesh);
				}
				
				//+++ TECHO +++
				if (_sceneWrapper.CONSTANTS.TECHO){
					
					params = {};
					params.puntos = space.contour.points; 
					params.bevelEnabled = true;
					params.material =  _materialBuilder.getMaterial("techo", nivel);
					params.extrudeValue = 0;
					obj3D = _meshBuilder.getShapeMesh(params);
					obj3D.position.y = space.height + altura  - 5;
					
					obj3D.geometry.dynamic = true
					obj3D.geometry.__dirtyVertices = true;
					obj3D.geometry.__dirtyNormals = true;

					obj3D.flipSided = true;

					for(var i = 0; i<obj3D.geometry.faces.length; i++) {
					    obj3D.geometry.faces[i].normal.x = -1*obj3D.geometry.faces[i].normal.x;
					    obj3D.geometry.faces[i].normal.y = -1*obj3D.geometry.faces[i].normal.y;
					    obj3D.geometry.faces[i].normal.z = -1*obj3D.geometry.faces[i].normal.z;
					}
					obj3D.geometry.computeVertexNormals();
					obj3D.geometry.computeFaceNormals();
					
					unMergedWalls.push(obj3D);
				}
			
			
				
			}
		};
		
		return {unMergedMeshes: unMergedSpaceMeshes, 
			sprites: sprites, 
			spritesBySpace: spritesBySpace,
			paredes: unMergedWalls
		};
	};
	
	
	

	
	
	function generatePortals(portals){
		var unMergedMeshes = []; //mallas de los spaces que vamos a mergear para mejorar eficiencia
		var unMergedWalls = []; 
		var stairs = []; //a parte porque no se pueden mergear (por ahora)
		
		var portal = null;
		for (var i = 0, l = portals.length; i < l; i++){
			portal = portals[i];
			var retObjs = generatePortal(portal); //retorna un arreglo de todas las mallas a mergear,
												  //y un arreglo de las mallas que se deben agregar a la escena sin mergear (escaleras)
			unMergedMeshes = unMergedMeshes.concat(retObjs.unMergedMeshes);
			unMergedWalls = unMergedWalls.concat(retObjs.unMergedWalls);
			stairs = stairs.concat(retObjs.stairs);
		}
		
		if (PROBANDO_FIRST_PERSON){
			return {unMergedMeshes: unMergedMeshes, unMergedWalls: unMergedWalls, stairs: stairs}; 
		}else{
			return {unMergedMeshes: [], unMergedWalls: unMergedWalls, stairs: stairs};
		}
		
	};
	
	
	
	function generatePortal(portal){
				
		var unMergedMeshes = [];
		var unMergedWalls = [];
		var stairs = [];
		
		function portalIntraFloor(){
			switch(portal.type){
				case "door": 
					//break;//TODO
					
					//console.log('portal.contour.length', portal.contour.length);
					if (!portal.contour.length){
						//console.log('entre', portal.contour);
						break; //TODO
					}
					
					
					//SHAPE PORTAL - objeto 3d (rectangulo que muestra el area correspondiente al portal)
					var params = {};
					//params.puntos = portal.contourScaled;
					params.puntos = portal.contour;
					params.bevelEnabled = true;
					params.material =  _materialBuilder.getMaterial("portal_interno", portal.piso.level);
	
					var mesh = _meshBuilder.getShapeMesh(params);
					mesh.position.y = portal.sourceSpace.height;//TODO
					unMergedMeshes.push(mesh);//agregamos el shape a la lista de mallas por mergear
				
					//+++ PAREDES +++
					if (_sceneWrapper.CONSTANTS.PAREDES){
						//paredes
						var altura = 0;
						var space = portal.sourceSpace;
						var nivel = space.piso.level;
						if (space.height < space.piso.referenceNode.height){
							altura = space.piso.referenceNode.height - space.height;
						}else{
//							altura = space.piso.referenceNode.height + 50  - space.height;
							altura = (nivel+1)*100 - space.height;
						} 
						
						//+++ PAREDES +++
						for (var i = 0, l = portal.paredes.length; i < l; i++){
							
							var params = {};
							params.puntos = portal.paredes[i];
							
							//si le pongo el mismo material que el piso, cuando quiero hacer invisibles las paredes, se hace invisible tambien el piso
							params.material = _materialBuilder.getMaterial("pared" /*space.type*/, nivel);
							params.altura = altura; //30
							var mesh = _meshBuilder.getWallMesh(params);
							
							unMergedWalls.push(mesh);
						}
					}
				
					break;
					
				case "ustair"://break;
				case "dstair":
					var params = {};
					params.tipo = portal.type;
					params.material =  _materialBuilder.getMaterial("escalon", portal.piso.level);
					params.puntos = portal.contour;
					params.alto = portal.differential; //altura de la escalera
					params.intra_floor = true;//TODO ya no dibujo mas escaleras inter_floor ???
					params.bevel = _sceneWrapper.CONSTANTS.BEVEL_ESCALERAS;
					params.paredesEscalera = portal.isUStair;
					
					var obj = _meshBuilder.getStairMesh(params);
					obj.translateY(portal.sourceSpace.relativeHeightToFloor);
					obj.portalID = portal.id;//harcodeado: id del portal
					obj.type = portal.type; //harcodeado: tipo del portal
					stairs.push(obj);
					
					
					
					
					//SHAPE PORTAL - objeto 3d (rectangulo que muestra el area correspondiente al portal) 1st person
					var params = {};
					params.puntos = portal.contourScaled;
					params.material =  _materialBuilder.getMaterial("portal_interno", portal.piso.level);
	
					var mesh = _meshBuilder.getShapeMesh(params);
					mesh.position.y = portal.piso.referenceNode.height;//TODO
				//		//TODO... no borrar esto, sirve para debug	
					unMergedMeshes.push(mesh);//agregamos el shape a la lista de mallas por mergear
					
					break;
				}
		};
		
		
		//distingo la clase del portal y de acuerdo a ello defino la forma en que se representaran
		switch (portal.clase){
			case "intra_floor": 
								portalIntraFloor(); 
								break;
		}
		//console.log("unMergedMeshes", unMergedMeshes)
		
		return {unMergedMeshes: unMergedMeshes, unMergedWalls: unMergedWalls, stairs: stairs}; 
		
		
	};//fin generar portales
};